#!/usr/bin/env python
# sysinfo, return hardware/OS information for the current machine. 
#
# TODO(thomas): Use platform library for as muchh as possible here.

import os
import platform
import re
import sys

def execute(command):
  return os.popen(command).read().strip()

class SysInfo(object):
  
  def __init__(self):
    self.os = self.KernelName()
    self.os_ver = self.KernelVersion()
    self.distro = self.DistroName()
    self.mount_dev = self.MountDevice()

  def Architecture(self):
    return platform.machine()

  def KernelName(self):
    return platform.system()
    
  def KernelVersion(self):
    return platform.uname()[2]
  
  def KernelMajorVersion(self):
    if self.os in ('Linux', 'SunOS'):
      return '.'.join(self.os_ver.split('.')[:2])
    else:
      return self.os_ver.split('.')[0]
  
  def KernelBuild(self):
    return execute("uname -v | sed s/'\@[a-z0-9\.]*'/@/g")
    
  def Bits(self):
    return platform.architecture()[0]

  def SystemModel(self):
    if self.distro == 'Solaris':
      return execute('/usr/sbin/prtdiag | grep "^System Configuration" | cut -d" " -f3-10')
    elif self.distro == 'Mac OS X':
      return execute("/usr/sbin/system_profiler SPHardwareDataType | grep 'Model Name' | cut -c19-80")
    elif self.os == 'HP-UX':
      return execute('model')
    elif self.os == 'AIX':
      return execute('uname -M')
    else:
      return 'Unknown'

  def Platform(self):
    tags = [self.os, self.KernelMajorVersion(), self.Architecture(), self.Bits()]
    if platform.libc_ver()[0]:
      tags.extend(platform.libc_ver())
    return '-'.join(tags)
    
  def L2CacheInKb(self):
    if self.distro == 'Mac OS X':
      return int(execute('/usr/sbin/system_profiler SPHardwareDataType | grep "L2 Cache"  | cut -d" " -f9')) * 1024
    elif self.os == 'Linux':
      return int(execute("grep 'cache size' /proc/cpuinfo | awk '{ print $4 }' | head -1"))
    else:
      return 'Unknown'
          
  def DistroName(self):
    if os.path.exists('/usr/sbin/sw_vers') or os.path.exists('/usr/sbin/system_profiler'):
      return 'Mac OS X'
    elif self.os == 'SunOS':
      minor = self.os_ver.split('.')[1]
      if minor > 5:
        return 'Solaris'
    elif os.path.exists('/usr/bin/lsb_release'):
        return execute('lsb_release -i -s')
    elif os.path.exists('/etc/release'):
        return execute('cat /etc/release')
    else:
      (distname, version, id) = platform.dist()
      if distname:
        return distname
      else:
        return self.os

  def DistroVersion(self):
    if self.distro == 'Mac OS X':
      return platform.mac_ver()[0]
    elif os.path.exists('/usr/bin/lsb_release'):
      return execute('lsb_release -r -s')
    elif self.os in ('IRIX', 'IRIX64'):
      return execute("uname -R | cut -d' ' -f2`")
    elif self.os == 'AIX':
      return execute('oslevel')
    else:
      (distname, version, id) = platform.dist()
      if version:
        return version
      else:
        return self.os_ver

  def RamInMegabytes(self):
    if self.os == 'Darwin':
      return int(execute('/usr/sbin/sysctl -n hw.memsize')) / 1024 / 1024
    elif self.distro == 'Solaris':
      return execute('/usr/sbin/prtconf | grep "Memory size" | cut -d" " -f3')      
    elif self.os == 'HP-UX':
      # this is horrible.
      return int(execute('grep Physical  /var/adm/syslog/syslog.log | tail -1 | cut -d: -f5 | cut -d" " -f2')) / 1024
    elif self.os in ('FreeBSD', 'OpenBSD', 'DragonFly'):
      return int(execute('/sbin/sysctl -n hw.physmem')) / 1024 / 1024
    elif self.os == 'NetBSD':
      return int(execute('grep "^total memory" /var/run/dmesg.boot| head -1 | cut -d" " -f4'))
    elif  self.os == 'Linux':
      return int(execute("grep '^MemTotal' /proc/meminfo | awk '{ print $2 }'")) / 1024      
    elif self.os in ('IRIX', 'IRIX64'):
      return int(execute('hinv -c memory | grep "^Main memory" | cut -d" " -f4'))
    else:
      return 0
      
  def ProcessorSpeedInMhz(self):
    if self.distro == 'Solaris':
      return execute('/usr/sbin/psrinfo -v | grep "operates at" | head -1 | cut -d" " -f8')      
    elif self.os == 'Darwin':
      return int(execute('/usr/sbin/sysctl -n hw.cpufrequency')) / 1000 / 1000
    elif self.os == 'FreeBSD': 
      return int(execute('/sbin/sysctl -n hw.clockrate'))
    elif self.os in ('DragonFly', 'OpenBSD', 'NetBSD'):
      return int(execute('/sbin/sysctl -n kern.ccpu'))
    elif self.os in ('IRIX', 'IRIX64'):
      return int(execute('hinv -c processor | grep MHZ | cut -d" " -f2'))
    elif self.os == 'AIX':
      return int(execute('lsattr -E -l proc0 | grep "^frequency" | cut -d" " -f2')) / 1024
    elif self.os == 'Linux':
      return int(float(execute("grep 'cpu MHz' /proc/cpuinfo | awk '{ print $4 }' | head -1")))
    else:
      return 0
      
  def ProcessorCount(self):
    if self.os == 'Darwin':
      return int(execute('/usr/sbin/sysctl -n hw.ncpu'))
    if self.os in ('FreeBSD', 'OpenBSD', 'DragonFly', 'NetBSD'):
      return int(execute('/sbin/sysctl -n hw.ncpu'))
    elif self.distro == 'Solaris':
      return int(execute("/usr/sbin/psrinfo | wc -l | awk '{ print $1}'`"))
    elif self.os == 'Linux':
      return int(execute('grep "^processor" /proc/cpuinfo | wc -l'))
    elif self.os in ('IRIX', 'IRIX64'):
      return int(execute('hinv -c processor | grep MHZ | cut -d" " -f1'))
    elif self.os == 'AIX':
      return int(execute('lscfg -l proc\* | grep -c Processor'))
    elif self.os == 'HP-UX':
      # cry.
      topfile = '/tmp/top.%s' % os.getpid()
      os.system('top -s1 -n1 -u -f %s' % topfile)      
      nprocs = int(execute('egrep "[0-9] .*%%" %s | grep -vc avg' % topfile))
      os.unlink(topfile)
      return nprocs
    else:
      return 0
          
  def ProcessorType(self):
    proc = 'Unknown'
    if self.os == 'Darwin':
      proc = execute('/usr/sbin/sysctl -n machdep.cpu.brand_string')
    elif self.os in ('FreeBSD', 'OpenBSD', 'DragonFly', 'NetBSD'):
      proc = execute('/sbin/sysctl -n hw.model')
    elif self.os in ('IRIX', 'IRIX64'):
      return execute('hinv -c processor | grep "^CPU" | cut -d" " -f2-3') 
    elif self.os == 'Linux':
      proc = execute('egrep "^family|^model name" /proc/cpuinfo | head -1 | cut -c14-90')
    # TODO(tstromberg): munge proc here
    proc = proc.replace('(R)', '')
    proc = proc.replace('(TM)', '')
    proc = proc.replace(' CPU', '')    
    return re.sub(' +', ' ', proc)
  
  def BusSpeedInMHz(self):
    if self.distro == 'Mac OS X':
      return int(float(execute('/usr/sbin/system_profiler SPHardwareDataType  | grep "Bus Speed" | cut -d" " -f9')) * 1000)
    else:
      return 'Unknown'
      
  def MountDevice(self):
    return execute("df . | grep '/dev' | awk '{ print $1 }'")
    
  def DiskId(self):
    match = re.match('.*\/([a-z]+\d+)', self.mount_dev)
    if match:
      return match.groups()[0]
    else:
      return 'Unknown'
    
  def VolumeType(self):
    disk_id = self.DiskId()
    if os.path.exists('/var/run/dmesg.boot'):
      return execute("grep '^%s:' /var/run/dmesg.boot | cut -d' ' -f2-50" % disk_id)
    elif self.distro == 'Mac OS X':
      is_raid = execute('diskutil listRAID | grep %s' % disk_id)
      if is_raid:
        # not necessarily mapped to thisk disk!
        raid_type = execute("diskutil listRAID | grep '^Type:' | awk '{ print $2 }'")
        disk_count = execute("diskutil listRAID | grep 'disk.*Online' | wc -l | awk '{ print $1 }'")
        return '%s-disk %s RAID' % (disk_count, raid_type)
    elif self.os == 'Linux' and 'VolGroup' in disk_id:
      return 'LVM'
    else:
      return 'Unknown'
      
  def FilesystemType(self):
    disk = self.mount_dev
    mount = execute('mount | grep "%s"' % disk)
    if 'type' in mount:
      match = re.search(' type (.*)', mount)
      if match:
        options = match.groups()[0]
        return re.sub(',errors=[\w-]+', '', options)
        
    bsd = execute('mount | grep "%s" | cut -d\( -f2 | cut -d\) -f1' % disk)
    if bsd:
      return bsd

if __name__ == "__main__": 
  si = SysInfo()
  if len(sys.argv) > 1:
    bound_method = getattr(si, sys.argv[1])
    print bound_method()
  else:
    for method in dir(si):
      if '_' not in method:
        try:
          bound_method = getattr(si, method)
          print '%-20.20s: %s' % (method, bound_method())
        except:
          print '%-20.20s: FAILED' % method
      
